Skip to content

ADGroup: Changing group membership management mechanism #620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Oct 10, 2020

Conversation

jeremyciak
Copy link
Contributor

@jeremyciak jeremyciak commented Jul 16, 2020

Pull Request (PR) description

This Pull Request is intended to change the way that the ADGroup resource manages group membership. The new implementation abandons usage of Add-ADGroupMember and Remove-ADGroupMember due to limitations with Foreign Security Principals. Instead we opt to utilize Set-ADGroup with the Add and Remove parameters, passing a hash object with the member key and a list of formatted SID values (e.g. - "<SID=SID_VALUE>").

Task list

  • Added an entry to the change log under the Unreleased section of the
    file CHANGELOG.md. Entry should say what was changed and how that
    affects users (if applicable), and reference the issue being resolved
    (if applicable).
  • Resource documentation added/updated in README.md.
  • Resource parameter descriptions added/updated in README.md, schema.mof
    and comment-based help.
  • Comment-based help added/updated.
  • Localization strings added/updated in all localization files as appropriate.
  • Examples appropriately added/updated.
  • Unit tests added/updated. See DSC Community Testing Guidelines.
  • Integration tests added/updated (where possible). See DSC Community Testing Guidelines.
  • New/changed code adheres to DSC Community Style Guidelines.

This change is Reviewable

@codecov
Copy link

codecov bot commented Jul 16, 2020

Codecov Report

Merging #620 into master will decrease coverage by 0%.
The diff coverage is 96%.

Impacted file tree graph

@@          Coverage Diff           @@
##           master   #620    +/-   ##
======================================
- Coverage      98%    97%    -1%     
======================================
  Files          24     25     +1     
  Lines        3110   3384   +274     
======================================
+ Hits         3049   3316   +267     
- Misses         61     68     +7     

@jeremyciak
Copy link
Contributor Author

There is a lot going on here so please bring on the commentary and suggestions. Also, my unit tests were mostly focused on code coverage so if there are some other tests that should be added please chime in.

@X-Guardian
Copy link
Contributor

@jeremyciak, you have used the master branch in your forked repository again. This makes it more difficult for me to review, because I can't add your fork as another remote in my local Git and switch to this branch, as it conflicts with the standard master branch. Please in future create a branch on your fork with a unique name and use that in your pull request. Thanks.

@jeremyciak jeremyciak closed this Jul 19, 2020
@jeremyciak
Copy link
Contributor Author

@X-Guardian, please refer to #621 instead as it is off of a branch from my fork. Sorry!

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 11 files reviewed, 11 unresolved discussions (waiting on @jeremyciak)


.vscode/settings.json, line 14 at r1 (raw file):

Quoted 7 lines of code…
    "files.defaultLanguage": "powershell",
    "[powershell]": {
        "editor.rulers": [ 120 ]
    },
    "[markdown]": {
        "editor.rulers": [ 120 ]
    },

Please remove these. We can consider setting these, but in their own PR.


source/DSCResources/MSFT_ADGroup/MSFT_ADGroup.psm1, line 846 at r1 (raw file):

                                MembershipAttribute = $MembershipAttribute
                                Parameters = $commonParameters
                                Action = 'Remove'

Please format all the hash tables you have added so that the attributes and values align. Format Document in VSCode will do it.


source/DSCResources/MSFT_ADGroup/MSFT_ADGroup.psm1, line 853 at r1 (raw file):

                        Write-Verbose -Message ($script:localizedData.AddingGroupMembers -f $Members.Count, $GroupName)

                        $addMemberSplat = @{

Please rename to $setADCommonGroupMemberParms and below.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 1138 at r1 (raw file):

    Assert-Module -ModuleName ActiveDirectory

    $resolveMembersSIDSplat = @{

Please rename this to $resolveMembersSecurityIdentifierParms


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2533 at r1 (raw file):

    .NOTES
        This is a helper function to allow for easier cross-domain AD group membership management based on SID.

... for easier one-way trust AD group membership ...


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2570 at r1 (raw file):

                    $translateSamAccountNameMessage = $script:localizedData.TranslatingMembershipAttribute -f
                        $MembershipAttribute, $member, $property
                    Write-Verbose -Message "  $($translateSamAccountNameMessage)" -Verbose:$verbose

You can split PowerShell lines naturally at the -f parameter so this and otherWrite-Verbose messages can be written as:

Write-Verbose -Message $script:localizedData.ResolvingMembershipAttributeValues -f
    $property, $MembershipAttribute -Verbose:$verbose

source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2572 at r1 (raw file):

        Write-Verbose -Message $resolvingSIDValuesMessage -Verbose:$verbose

        $getADObjectSplat = @{}

Can you rename this to $getADObjectParms


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2604 at r1 (raw file):

        }

        $Members | ForEach-Object -Process {

Can you change this to foreach ($member in $Members)


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2607 at r1 (raw file):

            $member = $_

            try

I don't like try/catch blocks covering large amounts of code. This should only cover calls that are likely to fail and deal with them explicitly. Can you please re-think this?


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2620 at r1 (raw file):

Quoted 4 lines of code…
                    $samAccountNameSplit = $member -split '\\'
                    $domain = $samAccountNameSplit[0]
                    $accountName = $samAccountNameSplit[1]
                    $ntAccount = [System.Security.Principal.NTAccount]::new($domain, $accountName)

You don't need to split the qualified account. This should work:

$ntAccount = [System.Security.Principal.NTAccount]::new($member)

source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2636 at r1 (raw file):

                    Write-Verbose -Message "  $($adLookupMessage)" -Verbose:$verbose

                    $getADObjectSplat['Filter'] = "$($MembershipAttribute) -eq '$($member)'"

Please rename to $getADObjectParms

@X-Guardian X-Guardian reopened this Jul 19, 2020
@X-Guardian
Copy link
Contributor

You have confused Reviewable having the two PR's with the same name, so please stick with one .

Copy link
Contributor Author

@jeremyciak jeremyciak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 0 of 11 files reviewed, 11 unresolved discussions (waiting on @X-Guardian)


.vscode/settings.json, line 14 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…
    "files.defaultLanguage": "powershell",
    "[powershell]": {
        "editor.rulers": [ 120 ]
    },
    "[markdown]": {
        "editor.rulers": [ 120 ]
    },

Please remove these. We can consider setting these, but in their own PR.

Done. I opened a separate issue for these settings.


source/DSCResources/MSFT_ADGroup/MSFT_ADGroup.psm1, line 846 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please format all the hash tables you have added so that the attributes and values align. Format Document in VSCode will do it.

Done.


source/DSCResources/MSFT_ADGroup/MSFT_ADGroup.psm1, line 853 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please rename to $setADCommonGroupMemberParms and below.

Done. All previously named $add...Splat and $remove...Splat hash objects have been renamed to $setADCommonGroupMemberParms


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 1138 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please rename this to $resolveMembersSecurityIdentifierParms

Done.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2533 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

... for easier one-way trust AD group membership ...

Done.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2570 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…
                    $translateSamAccountNameMessage = $script:localizedData.TranslatingMembershipAttribute -f
                        $MembershipAttribute, $member, $property
                    Write-Verbose -Message "  $($translateSamAccountNameMessage)" -Verbose:$verbose

You can split PowerShell lines naturally at the -f parameter so this and otherWrite-Verbose messages can be written as:

Write-Verbose -Message $script:localizedData.ResolvingMembershipAttributeValues -f
    $property, $MembershipAttribute -Verbose:$verbose

Done. I had also implemented my way to add some whitespace padding to the front of the message for some added formatting, however I have foregone that and implemented the requested change.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2572 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can you rename this to $getADObjectParms

Done.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2604 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can you change this to foreach ($member in $Members)

Done.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2607 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

I don't like try/catch blocks covering large amounts of code. This should only cover calls that are likely to fail and deal with them explicitly. Can you please re-think this?

Done. I switched up the try/catch implementation to only cover the Translate method scenario. In making this update I also implemented a few other compensating changes as well.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2620 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…
                    $samAccountNameSplit = $member -split '\\'
                    $domain = $samAccountNameSplit[0]
                    $accountName = $samAccountNameSplit[1]
                    $ntAccount = [System.Security.Principal.NTAccount]::new($domain, $accountName)

You don't need to split the qualified account. This should work:

$ntAccount = [System.Security.Principal.NTAccount]::new($member)

Done.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2636 at r1 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please rename to $getADObjectParms

Done.

@johlju johlju added the needs review The pull request needs a code review. label Jul 24, 2020
Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 2 of 11 files at r1, 1 of 5 files at r2.
Reviewable status: 2 of 10 files reviewed, 23 unresolved discussions (waiting on @jeremyciak)


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 1142 at r2 (raw file):

        Parameters = $Parameters
        PrepareForMembership = $true
        ErrorAction = 'Stop'

Please format this hash table so that the attributes and values align.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 1149 at r2 (raw file):

    }

    Write-Verbose -Message ($script:localizedData.SettingGroupMember -f $Action, $Parameters['Identity']) -Verbose

This doesn't quite work. The output ends up as:

VERBOSE: Adding members to/from AD group 'CN=TestGroup,OU=Fake,DC=contoso,DC=com'. (ADCOMMON0030)
VERBOSE: Removeing members to/from AD group 'CN=TestGroup,OU=Fake,DC=contoso,DC=com'. (ADCOMMON0030)

We don't actually need this anyway as the ADGroup resource already outputs a verbose messages ADG0003 and ADG0004 for this.

What might be useful is change this to a Write-Debug and output a message with the actual members that are added/removed.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 1151 at r2 (raw file):

    Write-Verbose -Message ($script:localizedData.SettingGroupMember -f $Action, $Parameters['Identity']) -Verbose

    Set-ADGroup @Parameters -ErrorAction 'Stop'

Can we have a try/catch block around this using New-InvalidOperationExceptionto output the error.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2489 at r2 (raw file):

    catch
    {
        $errorMessage = $script:localizedData.UnableToResolveMembershipAttribute -f

Please add brackets around the error message so that VS Code will properly format the code i.e.

        $errorMessage = ($script:localizedData.UnableToResolveMembershipAttribute -f
            'SamAccountName', 'ObjectSID', $ObjectSid)

source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2569 at r2 (raw file):

        $fspADContainer = 'CN=ForeignSecurityPrincipals'

        Write-Verbose -Message ($script:localizedData.ResolvingMembershipAttributeValues -f

Can you change this to a Write-Debug, as it doesn't need to be always output.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2610 at r2 (raw file):

                try
                {
                    Write-Verbose -Message ($script:localizedData.TranslatingMembershipAttribute -f

Can you change this to a Write-Debug, as it doesn't need to be always output and move out to before the try block.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2631 at r2 (raw file):

            elseif ($MembershipAttribute -eq 'DistinguishedName' -and ($member -split ',')[1] -eq $fspADContainer)
            {
                Write-Verbose -Message ($script:localizedData.ParsingCommonNameFromDN -f $member) -Verbose:$verbose

Can you change this to a Write-Debug, as it doesn't need to be always output.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2637 at r2 (raw file):

            else
            {
                Write-Verbose -Message ($script:localizedData.ADObjectPropertyLookup -f

Can you change this to a Write-Debug, as it doesn't need to be always output.


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2658 at r2 (raw file):

            else
            {
                Write-Warning -Message ($script:localizedData.UnableToResolveMembershipAttribute -f

This needs to be an exception not a warning.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 783 at r2 (raw file):

    }

    Describe 'ActiveDirectoryDsc.Common\Set-ADCommonGroupMember' {

Please follow the test pattern of 'ActiveDirectoryDsc.Common\Get-DomainControllerObject' i.e.

Context 'When ...' {
    BeforeAll {
        <Test Setup Mocks, Variables etc>
    }

    It 'Should ...' {
        <Should Tests>
        <Assert Mock Tests>
    }
}

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 784 at r2 (raw file):

    Describe 'ActiveDirectoryDsc.Common\Set-ADCommonGroupMember' {
        Mock -CommandName Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' }

No need for parameter filter on the Mock


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 786 at r2 (raw file):

        Mock -CommandName Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' }

        $groupMembersSplat = @{

Please rename to groupMembersParms and align the hashtable values.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 802 at r2 (raw file):

        }

        foreach ($action in @('Add', 'Remove')) {

Can we split this into two separate tests, one for 'Add;, one for 'Remove'.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 804 at r2 (raw file):

        foreach ($action in @('Add', 'Remove')) {
            It "Calls 'Set-ADGroup' with '$($action)' parameter when 'Action' parameter is specified as '$($action)'" {
                Mock -CommandName Set-ADGroup -ParameterFilter {

No need for parameter filter on the Mock


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 808 at r2 (raw file):

                }

                $setADCommonGroupMemberSplat = $groupMembersSplat.Clone()

Please rename to $setADCommonGroupMemberParms


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 813 at r2 (raw file):

                Set-ADCommonGroupMember @setADCommonGroupMemberSplat

                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier -Scope It

No need for -Scope It once you use contexts.
Please check it is mocked the correct number of times with -Exactly -Times $fakeADGroupMembersAsADObjects.Count


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 814 at r2 (raw file):

                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier -Scope It
                Assert-MockCalled -CommandName Set-ADGroup -ParameterFilter {

Please add Assert-MockCalled for Assert-Module.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 816 at r2 (raw file):

                Assert-MockCalled -CommandName Set-ADGroup -ParameterFilter {
                    (Get-Variable -Name $action -ValueOnly) -ne $null
                } -Exactly -Times 1 -Scope It

No need for '-Scope It` once you use contexts.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2101 at r2 (raw file):

    Describe 'ActiveDirectoryDsc.Common\Resolve-SamAccountName' {
        Context 'Properly formatted ObjectSid so an orphaned ForeignSecurityPrincipal is assumed' {

Can we use the pattern Context 'When....'


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2102 at r2 (raw file):

    Describe 'ActiveDirectoryDsc.Common\Resolve-SamAccountName' {
        Context 'Properly formatted ObjectSid so an orphaned ForeignSecurityPrincipal is assumed' {
            $objectSid = 'S-1-5-21-8562719340-2451078396-046517832-2106'

Please put test setup code in a BeforeAll block. Here and below.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2125 at r2 (raw file):

    }

    Describe 'ActiveDirectoryDsc.Common\Resolve-MembersSecurityIdentifier' {

Please put test setup code in a BeforeAll block. Here and below.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2134 at r2 (raw file):

        )

        It "Calls 'Get-ADObject' with 'Server' parameter when passed as part of the 'Parameters' parameter" {

Can we refactor using Contexts in the pattern:

Context 'When ...' {
    BeforeAll {
        <Test Setup Mocks, Variables etc>
    }

    It 'Should ...' {
        <Should Tests>
        <Assert Mock Tests>
    }
}

You can also use nested contexts if needed.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2135 at r2 (raw file):

        It "Calls 'Get-ADObject' with 'Server' parameter when passed as part of the 'Parameters' parameter" {
            Mock -CommandName Get-ADObject -ParameterFilter {

Don't use ParameterFilter on the Mock, here and below.

@X-Guardian X-Guardian added waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. and removed needs review The pull request needs a code review. labels Jul 29, 2020
@stale
Copy link

stale bot commented Aug 12, 2020

Labeling this pull request (PR) as abandoned since it has gone 14 days or more since the last update. An abandoned PR can be continued by another contributor. The abandoned label will be removed if work on this PR is taken up again.

@stale stale bot added the abandoned The pull request has been abandoned. label Aug 12, 2020
@X-Guardian
Copy link
Contributor

@jeremyciak, are you able to make the requested changes to this PR?

@jeremyciak
Copy link
Contributor Author

Yes, I have been working on these changes lately. Work has picked up significantly for me in the past 2 weeks which has diminished the amount of time I can allocate on this. I have some functional code close to ready but I'm having to get over the hump of some unit test stuff still. Hopefully I can poke at it a bit over the weekend.

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 of 11 files at r1, 2 of 8 files at r7.
Reviewable status: 3 of 12 files reviewed, 14 unresolved discussions (waiting on @jeremyciak and @X-Guardian)


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2137 at r5 (raw file):

Previously, jeremyciak (Jeremy Ciak) wrote…

The only purpose they serve is code coverage. Are we foregoing that?

Yes that's fine. We don't have tests for the other wrapper functions.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2173 at r7 (raw file):

                        Server = $testServer
                    }
                    WarningAction       = 'SilentlyContinue'

No need for WarningAction


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2176 at r7 (raw file):

                }

                Mock -CommandName Assert-Module

You can move this Assert-Module mock to a BeforeAll block in the Describe block and then there is no need to specify it in each Context block.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2180 at r7 (raw file):

                Mock -CommandName Get-ADObject -MockWith {
                    $mockADGroupMembersAsADObjects[0]
                }

Please add Mock and Assert-MockCalled for Resolve-SecurityIdentifier to all these tests.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2188 at r7 (raw file):

                Assert-MockCalled -CommandName Get-ADObject -ParameterFilter {
                    $Server -eq $testServer
                }

Please add -Exactly -Times 1 to all these Assert-MockCalled


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2191 at r7 (raw file):

                Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                     $ModuleName -eq 'ActiveDirectory'

Can we remove one indented space.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2209 at r7 (raw file):

                        Credential = $testCredentials
                    }
                    WarningAction       = 'SilentlyContinue'

No need for WarningAction


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2227 at r7 (raw file):

                Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                     $ModuleName -eq 'ActiveDirectory'

Can we remove one indented space.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2233 at r7 (raw file):

        Context "When 'MembershipAttribute' is 'SamAccountName'" {
            Context "When 'Resolve-SecurityIdentifier' fails" {

No need for this test as you are not handling this exception in the function.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2262 at r7 (raw file):

            It "Should throw an exception" {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } |
                    Should -Throw

Please change this to Should throw the correct exception and check the exception on the Should.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2279 at r7 (raw file):

                    }

                    $membersParamSplat = @{

Please rename to $resolveMembersSecurityIdentifierParms


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2292 at r7 (raw file):

                    }

                    Mock -CommandName Write-Debug -MockWith {

No need for this Write-Debug mock.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2315 at r7 (raw file):

                    Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                        $ModuleName -eq 'ActiveDirectory'
                    }

Please add Assert-MockCalled for each command mocked here and below with ParameterFilterif applicable and -Exactly -Times x.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2328 at r7 (raw file):

                        }

                        Mock -CommandName Write-Debug -MockWith {

No need for this Write-Debug mock.

Copy link
Contributor Author

@jeremyciak jeremyciak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 3 of 12 files reviewed, 14 unresolved discussions (waiting on @X-Guardian)


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2137 at r5 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Yes that's fine. We don't have tests for the other wrapper functions.

Done. I have removed the Pester block for this function.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2173 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for WarningAction

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2176 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

You can move this Assert-Module mock to a BeforeAll block in the Describe block and then there is no need to specify it in each Context block.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2180 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add Mock and Assert-MockCalled for Resolve-SecurityIdentifier to all these tests.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2188 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add -Exactly -Times 1 to all these Assert-MockCalled

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2191 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can we remove one indented space.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2209 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for WarningAction

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2227 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can we remove one indented space.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2233 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for this test as you are not handling this exception in the function.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2262 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please change this to Should throw the correct exception and check the exception on the Should.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2279 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please rename to $resolveMembersSecurityIdentifierParms

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2292 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for this Write-Debug mock.

I use this mock to increment the memberIndex so that I can pass an array of members into Resolve-MembersSecurityIdentifier and correctly maintain the index through the looping within.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2315 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add Assert-MockCalled for each command mocked here and below with ParameterFilterif applicable and -Exactly -Times x.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2328 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for this Write-Debug mock.

See above explanation

@jeremyciak
Copy link
Contributor Author

Sorry for the delay! I swore I had both committed my changes AND published my responses previously. Oops...

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 3 of 12 files reviewed, 25 unresolved discussions (waiting on @jeremyciak and @X-Guardian)


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2616 at r8 (raw file):

    begin
    {
        $verbose = $PSBoundParameters.Verbose -eq $true

This line is not longer need.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2292 at r7 (raw file):

Previously, jeremyciak (Jeremy Ciak) wrote…

I use this mock to increment the memberIndex so that I can pass an array of members into Resolve-MembersSecurityIdentifier and correctly maintain the index through the looping within.

If I comment out the line the tests still pass, so it is not achieving anything.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 31 at r8 (raw file):

    Set-StrictMode -Version 1.0

    $mockADGroupMembersAsADObjects = @(

As there are some many differing functions to test in this module, can we not share test variables between the tests, but duplicate and move the variables into the Describe block for each test that uses them.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 799 at r8 (raw file):

        Mock -CommandName Resolve-MembersSecurityIdentifier -MockWith {
            return $membershipSID['member']

No need for `return and please format as:

        Mock -CommandName Resolve-MembersSecurityIdentifier `
            -MockWith { $membershipSID['member'] }

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 813 at r8 (raw file):

                Set-ADCommonGroupMember @setADCommonGroupMemberParms

                Assert-MockCalled -CommandName Assert-Module

Please add -Exactly -Times 1


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 814 at r8 (raw file):

                Assert-MockCalled -CommandName Assert-Module
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier

Please add -Exactly -Times $setADCommonGroupMemberParms.Members.Count


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 817 at r8 (raw file):

                Assert-MockCalled -CommandName Set-ADGroup -ParameterFilter {
                    $Add -ne $null
                } -Exactly -Times 1

Please format as:

                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { $Add -ne $null } `
                    -Exactly -Times 1

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 832 at r8 (raw file):

                Set-ADCommonGroupMember @setADCommonGroupMemberParms

                Assert-MockCalled -CommandName Assert-Module

Please add -Exactly -Times 1


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 833 at r8 (raw file):

                Assert-MockCalled -CommandName Assert-Module
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier

Please add -Exactly -Times $setADCommonGroupMemberParms.Members.Count


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 836 at r8 (raw file):

                Assert-MockCalled -CommandName Set-ADGroup -ParameterFilter {
                    $Remove -ne $null
                } -Exactly -Times 1

Please format as:

                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { $Remove -ne $null } `
                    -Exactly -Times 1

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 840 at r8 (raw file):

        }

        Context "When 'Set-ADGroup' fails" {

Please change to When 'Set-ADGroup' throw an exception


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 843 at r8 (raw file):

            BeforeAll {
                Mock -CommandName Set-ADGroup -MockWith {
                    throw (New-Guid).Guid

Please change to throw 'Error'


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 844 at r8 (raw file):

                Mock -CommandName Set-ADGroup -MockWith {
                    throw (New-Guid).Guid
                }

Please add:

                $errorMessage = $script:localizedData.FailedToSetADGroupMembership -f
                    $groupMembersParms.Parameters.Identity

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 847 at r8 (raw file):

            }

            It "Should throw an exception" {

Please change to Should throw the correct exception


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 849 at r8 (raw file):

            It "Should throw an exception" {
                { Set-ADCommonGroupMember @groupMembersParms } |
                    Should -Throw

Please change to Should -Throw $errorMessage


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2144 at r8 (raw file):

                $memberADObjectSID = $mockADGroupMembersAsADObjects[($script:memberIndex)].ObjectSID
                $script:memberIndex++
                $script:resolveSecurityIdentifierCalls++

There is no point in having a count increment each time the function is called and then checking the function is called that number of times, you are not achieving anything! This should only be called when the MembershipAttribute is SamAccountNameand the number of times will be the number of members that are domain qualified.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2151 at r8 (raw file):

                $memberADObject = $mockADGroupMembersAsADObjects[$script:memberIndex]
                $script:memberIndex++
                $script:getADObjectCalls++

Same for this one. It is not achieving anything. The mock data needs to structured so you can check the correct count of this Mock.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2163 at r8 (raw file):

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects[$script:memberIndex].ObjectGUID

Specify the full $mockADGroupMembersAsADObjects.ObjectGUID array, and remove the $script:memberIndex.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2171 at r8 (raw file):

            }

            It "Calls 'Get-ADObject' with 'Server' parameter" {

Change this to It 'Calls the correct mocks' {


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2176 at r8 (raw file):

                Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                    $ModuleName -eq 'ActiveDirectory'
                } -Exactly -Times 1

To keep the correct indentation, please format here and below as:

                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2182 at r8 (raw file):

                Assert-MockCalled -CommandName Get-ADObject -ParameterFilter {
                    $Server -eq $testServer
                } -Exactly -Times 1

Change to -Exactly -Times $resolveMembersSecurityIdentifierParms.count


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2196 at r8 (raw file):

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects[$script:memberIndex].ObjectGUID

Specify the full $mockADGroupMembersAsADObjects.ObjectGUID array, and remove the $script:memberIndex.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2204 at r8 (raw file):

            }

            It "Calls 'Get-ADObject' with 'Credential' parameter" {

Change this to It 'Calls the correct mocks' {


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2215 at r8 (raw file):

                Assert-MockCalled -CommandName Get-ADObject -ParameterFilter {
                    $Credential -eq $testCredentials
                } -Exactly -Times 1

Change to -Exactly -Times $resolveMembersSecurityIdentifierParms.count


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2263 at r8 (raw file):

        }

        foreach ($attribute in @('SamAccountName', 'DistinguishedName', 'ObjectGUID', 'SID'))

Can you have a rethink about how these tests are structured? Trying to use a foreach loop makes the test logic complicated, and the Assert-Mocks -Times parameter is contrived.

Copy link
Contributor Author

@jeremyciak jeremyciak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 3 of 12 files reviewed, 25 unresolved discussions (waiting on @X-Guardian)


source/Modules/ActiveDirectoryDsc.Common/ActiveDirectoryDsc.Common.psm1, line 2616 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

This line is not longer need.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2292 at r7 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

If I comment out the line the tests still pass, so it is not achieving anything.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 31 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

As there are some many differing functions to test in this module, can we not share test variables between the tests, but duplicate and move the variables into the Describe block for each test that uses them.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 799 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

No need for `return and please format as:

        Mock -CommandName Resolve-MembersSecurityIdentifier `
            -MockWith { $membershipSID['member'] }

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 813 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add -Exactly -Times 1

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 814 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add -Exactly -Times $setADCommonGroupMemberParms.Members.Count

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 817 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please format as:

                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { $Add -ne $null } `
                    -Exactly -Times 1

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 832 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add -Exactly -Times 1

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 833 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add -Exactly -Times $setADCommonGroupMemberParms.Members.Count

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 836 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please format as:

                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { $Remove -ne $null } `
                    -Exactly -Times 1

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 840 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please change to When 'Set-ADGroup' throw an exception

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 843 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please change to throw 'Error'

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 844 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please add:

                $errorMessage = $script:localizedData.FailedToSetADGroupMembership -f
                    $groupMembersParms.Parameters.Identity

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 847 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please change to Should throw the correct exception

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 849 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Please change to Should -Throw $errorMessage

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2144 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

There is no point in having a count increment each time the function is called and then checking the function is called that number of times, you are not achieving anything! This should only be called when the MembershipAttribute is SamAccountNameand the number of times will be the number of members that are domain qualified.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2151 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Same for this one. It is not achieving anything. The mock data needs to structured so you can check the correct count of this Mock.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2163 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Specify the full $mockADGroupMembersAsADObjects.ObjectGUID array, and remove the $script:memberIndex.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2171 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Change this to It 'Calls the correct mocks' {

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2176 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…
                Assert-MockCalled -CommandName Assert-Module -ParameterFilter {
                    $ModuleName -eq 'ActiveDirectory'
                } -Exactly -Times 1

To keep the correct indentation, please format here and below as:

                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2182 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Change to -Exactly -Times $resolveMembersSecurityIdentifierParms.count

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2196 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Specify the full $mockADGroupMembersAsADObjects.ObjectGUID array, and remove the $script:memberIndex.

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2204 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Change this to It 'Calls the correct mocks' {

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2215 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Change to -Exactly -Times $resolveMembersSecurityIdentifierParms.count

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2263 at r8 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can you have a rethink about how these tests are structured? Trying to use a foreach loop makes the test logic complicated, and the Assert-Mocks -Times parameter is contrived.

Done.

@jeremyciak
Copy link
Contributor Author

@X-Guardian, I just want to double check that this is not waiting on me because last time I assumed it wasn't I was wrong and it just sat for a week until I realized that was the case.

@X-Guardian
Copy link
Contributor

Hi @jeremyciak, yes it is with me. You can see by looking at the reviewable check, and it tells you who the discussions are pending with.

@jeremyciak
Copy link
Contributor Author

Alright awesome, thanks!

@stale
Copy link

stale bot commented Sep 9, 2020

Labeling this pull request (PR) as abandoned since it has gone 14 days or more since the last update. An abandoned PR can be continued by another contributor. The abandoned label will be removed if work on this PR is taken up again.

@stale stale bot added the abandoned The pull request has been abandoned. label Sep 9, 2020
@jeremyciak
Copy link
Contributor Author

@X-Guardian do you think you will have any bandwidth to circle back on this over the weekend?

@X-Guardian
Copy link
Contributor


tests/Unit/MSFT_ADGroup.Tests.ps1, line 756 at r9 (raw file):

                Assert-MockCalled -CommandName Set-ADCommonGroupMember -ParameterFilter {
                    $Action -eq 'Add'
                } -Scope It -Exactly -Times 1

Can we format the Assert-MockCalled as:

Assert-MockCalled -CommandName Set-ADCommonGroupMember `
    -ParameterFilter { $Action -eq 'Add' } `
    -Scope It -Exactly -Times 1

here and below to keep the indentation correct.

@X-Guardian
Copy link
Contributor


tests/Unit/MSFT_ADGroup.Tests.ps1, line 890 at r9 (raw file):

                Mock -CommandName Set-ADCommonGroupMember -ParameterFilter {
                    $Action -eq 'Add'
                }

Can we format this as:

Mock -CommandName Set-ADCommonGroupMember `
    -ParameterFilter { $Action eq - 'Add' }

here and below to keep the indentation correct.

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 3 of 12 files reviewed, 4 unresolved discussions (waiting on @jeremyciak and @X-Guardian)


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 751 at r9 (raw file):

    }

    Describe 'ActiveDirectoryDsc.Common\Set-ADCommonGroupMember' {

Here is a refactored/reformatted Set-ADCommonGroupMember test:

Describe 'ActiveDirectoryDsc.Common\Set-ADCommonGroupMember' {
        BeforeAll {
            $mockADGroupMembersAsADObjects = @(
                [PSCustomObject] @{
                    DistinguishedName = 'CN=User 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = 'a97cc867-0c9e-4928-8387-0dba0c883b8e'
                    SamAccountName    = 'USER1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1106'
                    ObjectClass       = 'user'
                }
                [PSCustomObject] @{
                    DistinguishedName = 'CN=Group 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = 'e2328767-2673-40b2-b3b7-ce9e6511df06'
                    SamAccountName    = 'GROUP1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1206'
                    ObjectClass       = 'group'
                }
                [PSCustomObject] @{
                    DistinguishedName = 'CN=Computer 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = '42f9d607-0934-4afc-bb91-bdf93e07cbfc'
                    SamAccountName    = 'COMPUTER1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-6606'
                    ObjectClass       = 'computer'
                }
                # This entry is used to represent a group member from a one-way trusted domain
                [PSCustomObject] @{
                    DistinguishedName = 'CN=S-1-5-21-8562719340-2451078396-046517832-2106,CN=ForeignSecurityPrincipals,DC=contoso,DC=com'
                    ObjectGUID        = '6df78e9e-c795-4e67-a626-e17f1b4a0d8b'
                    SamAccountName    = 'ADATUM\USER1'
                    ObjectSID         = 'S-1-5-21-8562719340-2451078396-046517832-2106'
                    ObjectClass       = 'foreignSecurityPrincipal'
                }
            )

            $setADCommonGroupMemberParms = @{
                Members             = $mockADGroupMembersAsADObjects.DistinguishedName
                MembershipAttribute = 'DistinguishedName'
                Parameters          = @{
                    Identity = 'CN=TestGroup,OU=Fake,DC=contoso,DC=com'
                }
            }

            $membershipSID = @{
                member = $mockADGroupMembersAsADObjects.ObjectSID | ForEach-Object -Process { "<SID=$($_)>" }
            }

            Mock -CommandName Assert-Module
            Mock -CommandName Resolve-MembersSecurityIdentifier -MockWith { $membershipSID['member'] }
            Mock -CommandName Set-ADGroup
        }

        Context "When the 'Action' parameter is specified as 'Add'" {
            BeforeAll {
                $setADCommonGroupMemberAddParms = $setADCommonGroupMemberParms.Clone()
                $setADCommonGroupMemberAddParms['Action'] = 'Add'
            }

            It 'Should not throw' {
                { Set-ADCommonGroupMember @setADCommonGroupMemberAddParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier `
                    -Exactly -Times $setADCommonGroupMemberAddParms.Members.Count
                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { `
                        $Add -ne $null -and `
                        $Identity -eq $setADCommonGroupMemberAddParms.Parameters.Identity } `
                    -Exactly -Times 1
            }
        }

        Context "When 'Action' parameter is specified as 'Remove'" {
            BeforeAll {
                $setADCommonGroupMemberRemoveParms = $setADCommonGroupMemberParms.Clone()
                $setADCommonGroupMemberRemoveParms['Action'] = 'Remove'
            }

            It 'Should not throw' {
                { Set-ADCommonGroupMember @setADCommonGroupMemberRemoveParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier `
                    -Exactly -Times $setADCommonGroupMemberRemoveParms.Members.Count
                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { `
                        $Remove -ne $null -and `
                        $Identity -eq $setADCommonGroupMemberRemoveParms.Parameters.Identity } `
                    -Exactly -Times 1
            }
        }

        Context "When 'Set-ADGroup' throws an exception" {
            BeforeAll {
                Mock -CommandName Set-ADGroup -MockWith { throw 'Error' }

                $errorMessage = $script:localizedData.FailedToSetADGroupMembership -f
                    $setADCommonGroupMemberParms.Parameters.Identity
            }

            It "Should throw the correct exception" {
                { Set-ADCommonGroupMember @setADCommonGroupMemberParms } |
                    Should -Throw $errorMessage
            }
        }
    }

tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2137 at r9 (raw file):

    }

    Describe 'ActiveDirectoryDsc.Common\Resolve-MembersSecurityIdentifier' {

Here is a refactored/reformatted Resolve-MembersSecurityIdentifier test:

Describe 'ActiveDirectoryDsc.Common\Resolve-MembersSecurityIdentifier' {
        $mockADGroupMembersAsADObjects = @(
            [PSCustomObject] @{
                DistinguishedName = 'CN=User 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = 'a97cc867-0c9e-4928-8387-0dba0c883b8e'
                SamAccountName    = 'USER1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1106'
                ObjectClass       = 'user'
            }
            [PSCustomObject] @{
                DistinguishedName = 'CN=Group 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = 'e2328767-2673-40b2-b3b7-ce9e6511df06'
                SamAccountName    = 'GROUP1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1206'
                ObjectClass       = 'group'
            }
            [PSCustomObject] @{
                DistinguishedName = 'CN=Computer 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = '42f9d607-0934-4afc-bb91-bdf93e07cbfc'
                SamAccountName    = 'COMPUTER1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-6606'
                ObjectClass       = 'computer'
            }
            # This entry is used to represent a group member from a one-way trusted domain
            [PSCustomObject] @{
                DistinguishedName = 'CN=S-1-5-21-8562719340-2451078396-046517832-2106,CN=ForeignSecurityPrincipals,DC=contoso,DC=com'
                ObjectGUID        = '6df78e9e-c795-4e67-a626-e17f1b4a0d8b'
                SamAccountName    = 'ADATUM\USER1'
                ObjectSID         = 'S-1-5-21-8562719340-2451078396-046517832-2106'
                ObjectClass       = 'foreignSecurityPrincipal'
            }
        )

        BeforeAll {
            $script:memberIndex = 0

            Mock -CommandName Assert-Module

            Mock -CommandName Resolve-SecurityIdentifier -MockWith {
                $memberADObjectSID = $mockADGroupMembersAsADObjects[($script:memberIndex)].ObjectSID
                $script:memberIndex++
                return $memberADObjectSID
            }

            Mock -CommandName Get-ADObject -MockWith {
                $memberADObject = $mockADGroupMembersAsADObjects[$script:memberIndex]
                $script:memberIndex++
                return $memberADObject
            }
        }

        Context "When 'Server' is passed as part of the 'Parameters' parameter" {
            BeforeAll {
                $testServer = 'TESTDC'
                $membershipAttribute = 'ObjectGUID'

                $script:memberIndex = 0

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                    Parameters          = @{
                        Server = $testServer
                    }
                }
            }

            It 'Should not throw' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -ParameterFilter { $Server -eq $testServer } `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When 'Credential' is passed as part of the 'Parameters' parameter" {
            BeforeAll {
                $testCredentials = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @(
                    'DummyUser',
                    (ConvertTo-SecureString -String 'DummyPassword' -AsPlainText -Force)
                )
                $membershipAttribute = 'ObjectGUID'

                $script:memberIndex = 0

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                    Parameters          = @{
                        Credential = $testCredentials
                    }
                }
            }

            It 'Should not throw' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -ParameterFilter { $Credential -eq $testCredentials } `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When 'Get-ADObject' returns no value" {
            BeforeAll {
                $membershipAttribute = 'ObjectGUID'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects[0].$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $errorMessage = ($script:localizedData.UnableToResolveMembershipAttribute -f
                    'ObjectSID', $membershipAttribute, $mockADGroupMembersAsADObjects[0].$membershipAttribute)

                Mock -CommandName Resolve-SecurityIdentifier
                Mock -CommandName Get-ADObject
            }

            It 'Should throw the correct exception' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } |
                    Should -Throw $errorMessage
            }
        }

        Context "When MembershipAttribute 'SamAccountName' is specified" {
            BeforeAll {
                $membershipAttribute = 'SamAccountName'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $resolveSecurityIdentifierCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -Match '\\').Count

                $getADObjectCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -NotMatch '\\').Count

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times $resolveSecurityIdentifierCount
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $getADObjectCount
            }
        }

        Context "When MembershipAttribute 'DistinguishedName' is specified" {
            BeforeAll {
                $membershipAttribute = 'DistinguishedName'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $getADObjectCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -NotMatch 'CN=ForeignSecurityPrincipals').Count

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $getADObjectCount
            }
        }

        Context "When MembershipAttribute 'ObjectGUID' is specified" {
            BeforeAll {
                $membershipAttribute = 'ObjectGUID'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $script:memberIndex = 0
            }

            It 'Should Return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When MembershipAttribute 'SID' is specified" {
            BeforeAll {
                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.ObjectSID
                    MembershipAttribute = 'SID'
                }

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times 0
            }
        }

        Context "When 'PrepareForMembership' is specified" {
            Context "When the MembershipAttribute specified is not 'SID'" {
                BeforeAll {
                    $membershipAttribute = 'ObjectGUID'

                    $resolveMembersSecurityIdentifierParms = @{
                        Members              = $mockADGroupMembersAsADObjects.$membershipAttribute
                        MembershipAttribute  = $membershipAttribute
                        PrepareForMembership = $true
                    }
                }

                It 'Should return the correct result' {
                    $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                    for ($i = 0; $i -lt $result.Count; $i++)
                    {
                        $result[$i] | Should -Be "<SID=$($mockADGroupMembersAsADObjects[$i].ObjectSID)>"
                    }
                }
            }

            Context "When MembershipAttribute specified is 'SID'" {
                BeforeAll {
                    $resolveMembersSecurityIdentifierParms = @{
                        Members              = $mockADGroupMembersAsADObjects.ObjectSID
                        MembershipAttribute  = 'SID'
                        PrepareForMembership = $true
                    }
                }

                It 'Should return the correct result' {
                    $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                    for ($i = 0; $i -lt $result.Count; $i++)
                    {
                        $result[$i] | Should -Be "<SID=$($mockADGroupMembersAsADObjects[$i].ObjectSID)>"
                    }
                }
            }
        }
    }

@stale stale bot removed the abandoned The pull request has been abandoned. label Sep 27, 2020
@X-Guardian
Copy link
Contributor

Can you add an example ADGroup resource file with the qualified SamAccountName name format required for one way external trusts. Something like the following:

<#PSScriptInfo
.VERSION 1.0.0
.GUID f2ecc331-e242-4204-a6b1-54fd68c852b7
.AUTHOR DSC Community
.COMPANYNAME DSC Community
.COPYRIGHT DSC Community contributors. All rights reserved.
.TAGS DSCConfiguration
.LICENSEURI https://github.com/dsccommunity/ActiveDirectoryDsc/blob/master/LICENSE
.PROJECTURI https://github.com/dsccommunity/ActiveDirectoryDsc
.ICONURI https://dsccommunity.org/images/DSC_Logo_300p.png
.RELEASENOTES
Initial release
#>

#Requires -Module ActiveDirectoryDsc

<#
    .DESCRIPTION
        This configuration will create a new domain-local group in contoso with
        two members; one from the contoso domain and one from the fabrikam domain.
        This qualified SamAccountName format is required if any of the users are in a 
        one-way trusted forest/external domain.
#>
Configuration ADGroup_NewGroupOneWayTrust_Config
{
    Import-DscResource -ModuleName ActiveDirectoryDsc

    node localhost
    {
        ADGroup 'ExampleExternalTrustGroup'
        {
            GroupName           = 'ExampleExternalTrustGroup'
            GroupScope          = 'DomainLocal'
            MembershipAttribute = 'SamAccountName'
            Members             = @(
                'contoso\john'
                'fabrikam\toby'
            )
        }
    }
}

Copy link
Contributor Author

@jeremyciak jeremyciak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: 3 of 13 files reviewed, 4 unresolved discussions (waiting on @X-Guardian)


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 751 at r9 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Here is a refactored/reformatted Set-ADCommonGroupMember test:

Describe 'ActiveDirectoryDsc.Common\Set-ADCommonGroupMember' {
        BeforeAll {
            $mockADGroupMembersAsADObjects = @(
                [PSCustomObject] @{
                    DistinguishedName = 'CN=User 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = 'a97cc867-0c9e-4928-8387-0dba0c883b8e'
                    SamAccountName    = 'USER1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1106'
                    ObjectClass       = 'user'
                }
                [PSCustomObject] @{
                    DistinguishedName = 'CN=Group 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = 'e2328767-2673-40b2-b3b7-ce9e6511df06'
                    SamAccountName    = 'GROUP1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1206'
                    ObjectClass       = 'group'
                }
                [PSCustomObject] @{
                    DistinguishedName = 'CN=Computer 1,CN=Users,DC=contoso,DC=com'
                    ObjectGUID        = '42f9d607-0934-4afc-bb91-bdf93e07cbfc'
                    SamAccountName    = 'COMPUTER1'
                    ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-6606'
                    ObjectClass       = 'computer'
                }
                # This entry is used to represent a group member from a one-way trusted domain
                [PSCustomObject] @{
                    DistinguishedName = 'CN=S-1-5-21-8562719340-2451078396-046517832-2106,CN=ForeignSecurityPrincipals,DC=contoso,DC=com'
                    ObjectGUID        = '6df78e9e-c795-4e67-a626-e17f1b4a0d8b'
                    SamAccountName    = 'ADATUM\USER1'
                    ObjectSID         = 'S-1-5-21-8562719340-2451078396-046517832-2106'
                    ObjectClass       = 'foreignSecurityPrincipal'
                }
            )

            $setADCommonGroupMemberParms = @{
                Members             = $mockADGroupMembersAsADObjects.DistinguishedName
                MembershipAttribute = 'DistinguishedName'
                Parameters          = @{
                    Identity = 'CN=TestGroup,OU=Fake,DC=contoso,DC=com'
                }
            }

            $membershipSID = @{
                member = $mockADGroupMembersAsADObjects.ObjectSID | ForEach-Object -Process { "<SID=$($_)>" }
            }

            Mock -CommandName Assert-Module
            Mock -CommandName Resolve-MembersSecurityIdentifier -MockWith { $membershipSID['member'] }
            Mock -CommandName Set-ADGroup
        }

        Context "When the 'Action' parameter is specified as 'Add'" {
            BeforeAll {
                $setADCommonGroupMemberAddParms = $setADCommonGroupMemberParms.Clone()
                $setADCommonGroupMemberAddParms['Action'] = 'Add'
            }

            It 'Should not throw' {
                { Set-ADCommonGroupMember @setADCommonGroupMemberAddParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier `
                    -Exactly -Times $setADCommonGroupMemberAddParms.Members.Count
                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { `
                        $Add -ne $null -and `
                        $Identity -eq $setADCommonGroupMemberAddParms.Parameters.Identity } `
                    -Exactly -Times 1
            }
        }

        Context "When 'Action' parameter is specified as 'Remove'" {
            BeforeAll {
                $setADCommonGroupMemberRemoveParms = $setADCommonGroupMemberParms.Clone()
                $setADCommonGroupMemberRemoveParms['Action'] = 'Remove'
            }

            It 'Should not throw' {
                { Set-ADCommonGroupMember @setADCommonGroupMemberRemoveParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-MembersSecurityIdentifier `
                    -Exactly -Times $setADCommonGroupMemberRemoveParms.Members.Count
                Assert-MockCalled -CommandName Set-ADGroup `
                    -ParameterFilter { `
                        $Remove -ne $null -and `
                        $Identity -eq $setADCommonGroupMemberRemoveParms.Parameters.Identity } `
                    -Exactly -Times 1
            }
        }

        Context "When 'Set-ADGroup' throws an exception" {
            BeforeAll {
                Mock -CommandName Set-ADGroup -MockWith { throw 'Error' }

                $errorMessage = $script:localizedData.FailedToSetADGroupMembership -f
                    $setADCommonGroupMemberParms.Parameters.Identity
            }

            It "Should throw the correct exception" {
                { Set-ADCommonGroupMember @setADCommonGroupMemberParms } |
                    Should -Throw $errorMessage
            }
        }
    }

Done.


tests/Unit/ActiveDirectoryDsc.Common.Tests.ps1, line 2137 at r9 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Here is a refactored/reformatted Resolve-MembersSecurityIdentifier test:

Describe 'ActiveDirectoryDsc.Common\Resolve-MembersSecurityIdentifier' {
        $mockADGroupMembersAsADObjects = @(
            [PSCustomObject] @{
                DistinguishedName = 'CN=User 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = 'a97cc867-0c9e-4928-8387-0dba0c883b8e'
                SamAccountName    = 'USER1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1106'
                ObjectClass       = 'user'
            }
            [PSCustomObject] @{
                DistinguishedName = 'CN=Group 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = 'e2328767-2673-40b2-b3b7-ce9e6511df06'
                SamAccountName    = 'GROUP1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-1206'
                ObjectClass       = 'group'
            }
            [PSCustomObject] @{
                DistinguishedName = 'CN=Computer 1,CN=Users,DC=contoso,DC=com'
                ObjectGUID        = '42f9d607-0934-4afc-bb91-bdf93e07cbfc'
                SamAccountName    = 'COMPUTER1'
                ObjectSID         = 'S-1-5-21-1131554080-2861379300-292325817-6606'
                ObjectClass       = 'computer'
            }
            # This entry is used to represent a group member from a one-way trusted domain
            [PSCustomObject] @{
                DistinguishedName = 'CN=S-1-5-21-8562719340-2451078396-046517832-2106,CN=ForeignSecurityPrincipals,DC=contoso,DC=com'
                ObjectGUID        = '6df78e9e-c795-4e67-a626-e17f1b4a0d8b'
                SamAccountName    = 'ADATUM\USER1'
                ObjectSID         = 'S-1-5-21-8562719340-2451078396-046517832-2106'
                ObjectClass       = 'foreignSecurityPrincipal'
            }
        )

        BeforeAll {
            $script:memberIndex = 0

            Mock -CommandName Assert-Module

            Mock -CommandName Resolve-SecurityIdentifier -MockWith {
                $memberADObjectSID = $mockADGroupMembersAsADObjects[($script:memberIndex)].ObjectSID
                $script:memberIndex++
                return $memberADObjectSID
            }

            Mock -CommandName Get-ADObject -MockWith {
                $memberADObject = $mockADGroupMembersAsADObjects[$script:memberIndex]
                $script:memberIndex++
                return $memberADObject
            }
        }

        Context "When 'Server' is passed as part of the 'Parameters' parameter" {
            BeforeAll {
                $testServer = 'TESTDC'
                $membershipAttribute = 'ObjectGUID'

                $script:memberIndex = 0

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                    Parameters          = @{
                        Server = $testServer
                    }
                }
            }

            It 'Should not throw' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -ParameterFilter { $Server -eq $testServer } `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When 'Credential' is passed as part of the 'Parameters' parameter" {
            BeforeAll {
                $testCredentials = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @(
                    'DummyUser',
                    (ConvertTo-SecureString -String 'DummyPassword' -AsPlainText -Force)
                )
                $membershipAttribute = 'ObjectGUID'

                $script:memberIndex = 0

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                    Parameters          = @{
                        Credential = $testCredentials
                    }
                }
            }

            It 'Should not throw' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } | Should -Not -Throw
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -ParameterFilter { $Credential -eq $testCredentials } `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When 'Get-ADObject' returns no value" {
            BeforeAll {
                $membershipAttribute = 'ObjectGUID'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects[0].$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $errorMessage = ($script:localizedData.UnableToResolveMembershipAttribute -f
                    'ObjectSID', $membershipAttribute, $mockADGroupMembersAsADObjects[0].$membershipAttribute)

                Mock -CommandName Resolve-SecurityIdentifier
                Mock -CommandName Get-ADObject
            }

            It 'Should throw the correct exception' {
                { Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms } |
                    Should -Throw $errorMessage
            }
        }

        Context "When MembershipAttribute 'SamAccountName' is specified" {
            BeforeAll {
                $membershipAttribute = 'SamAccountName'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $resolveSecurityIdentifierCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -Match '\\').Count

                $getADObjectCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -NotMatch '\\').Count

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times $resolveSecurityIdentifierCount
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $getADObjectCount
            }
        }

        Context "When MembershipAttribute 'DistinguishedName' is specified" {
            BeforeAll {
                $membershipAttribute = 'DistinguishedName'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $getADObjectCount = @($mockADGroupMembersAsADObjects |
                    Where-Object -Property $membershipAttribute -NotMatch 'CN=ForeignSecurityPrincipals').Count

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $getADObjectCount
            }
        }

        Context "When MembershipAttribute 'ObjectGUID' is specified" {
            BeforeAll {
                $membershipAttribute = 'ObjectGUID'

                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.$membershipAttribute
                    MembershipAttribute = $membershipAttribute
                }

                $script:memberIndex = 0
            }

            It 'Should Return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times $mockADGroupMembersAsADObjects.Count
            }
        }

        Context "When MembershipAttribute 'SID' is specified" {
            BeforeAll {
                $resolveMembersSecurityIdentifierParms = @{
                    Members             = $mockADGroupMembersAsADObjects.ObjectSID
                    MembershipAttribute = 'SID'
                }

                $script:memberIndex = 0
            }

            It 'Should return the correct result' {
                $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                for ($i = 0; $i -lt $result.Count; $i++)
                {
                    $result[$i] | Should -Be $mockADGroupMembersAsADObjects[$i].ObjectSID
                }
            }

            It 'Should call the expected mocks' {
                Assert-MockCalled -CommandName Assert-Module `
                    -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } `
                    -Exactly -Times 1
                Assert-MockCalled -CommandName Resolve-SecurityIdentifier `
                    -Exactly -Times 0
                Assert-MockCalled -CommandName Get-ADObject `
                    -Exactly -Times 0
            }
        }

        Context "When 'PrepareForMembership' is specified" {
            Context "When the MembershipAttribute specified is not 'SID'" {
                BeforeAll {
                    $membershipAttribute = 'ObjectGUID'

                    $resolveMembersSecurityIdentifierParms = @{
                        Members              = $mockADGroupMembersAsADObjects.$membershipAttribute
                        MembershipAttribute  = $membershipAttribute
                        PrepareForMembership = $true
                    }
                }

                It 'Should return the correct result' {
                    $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                    for ($i = 0; $i -lt $result.Count; $i++)
                    {
                        $result[$i] | Should -Be "<SID=$($mockADGroupMembersAsADObjects[$i].ObjectSID)>"
                    }
                }
            }

            Context "When MembershipAttribute specified is 'SID'" {
                BeforeAll {
                    $resolveMembersSecurityIdentifierParms = @{
                        Members              = $mockADGroupMembersAsADObjects.ObjectSID
                        MembershipAttribute  = 'SID'
                        PrepareForMembership = $true
                    }
                }

                It 'Should return the correct result' {
                    $result = Resolve-MembersSecurityIdentifier @resolveMembersSecurityIdentifierParms

                    for ($i = 0; $i -lt $result.Count; $i++)
                    {
                        $result[$i] | Should -Be "<SID=$($mockADGroupMembersAsADObjects[$i].ObjectSID)>"
                    }
                }
            }
        }
    }

Done.


tests/Unit/MSFT_ADGroup.Tests.ps1, line 756 at r9 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can we format the Assert-MockCalled as:

Assert-MockCalled -CommandName Set-ADCommonGroupMember `
    -ParameterFilter { $Action -eq 'Add' } `
    -Scope It -Exactly -Times 1

here and below to keep the indentation correct.

Done.


tests/Unit/MSFT_ADGroup.Tests.ps1, line 890 at r9 (raw file):

Previously, X-Guardian (Simon Heather) wrote…

Can we format this as:

Mock -CommandName Set-ADCommonGroupMember `
    -ParameterFilter { $Action eq - 'Add' }

here and below to keep the indentation correct.

Done.

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 of 5 files at r2, 1 of 4 files at r3, 3 of 8 files at r7, 1 of 2 files at r9, 1 of 1 files at r10, 1 of 3 files at r11.
Reviewable status: 11 of 13 files reviewed, all discussions resolved

Copy link
Contributor

@X-Guardian X-Guardian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 2 of 3 files at r11.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved

@X-Guardian
Copy link
Contributor

Fantastic work @jeremyciak, and thanks for your patience. LGTM!

@X-Guardian X-Guardian merged commit 87b1308 into dsccommunity:master Oct 10, 2020
@X-Guardian X-Guardian removed the waiting for code fix A review left open comments, and the pull request is waiting for changes to be pushed by the author. label Oct 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ADGroup: Proposal to Support Group Members Across One-Way External Trusts
3 participants